#pragma once

#include <stdlib.h>
#include <type_traits>

#ifdef PUNCH_MSGPACK
#include <msgpack.hpp>
#endif

namespace mdtx::punch
{
    template <typename T>
    inline bool test_bit(T base, size_t bit)
    {
        static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "T must be unsigned integral");
        return base & (T(1) << bit);
    }

    template <typename T>
    inline void set_bit(T base, size_t bit)
    {
        static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "T must be unsigned integral");
        base |= (T(1) << bit);
    }

    template <typename T>
    inline void reset_bit(T base, size_t bit)
    {
        static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "T must be unsigned integral");
        base &= ~(T(1) << bit);
    }

    template <typename QuoteType>
    struct BaseOrder : public QuoteType
    {
        using base = QuoteType;
        using typename base::id_type;
        using typename base::quant_type;

        using bitset_type = size_t;
        bitset_type info;

#ifdef PUNCH_MSGPACK
        MSGPACK_DEFINE_ARRAY(MSGPACK_BASE_ARRAY(base), info);

        // msgpack::object::as requires default ctor
        BaseOrder()
        {
        }
#endif

        // basic properties
        static constexpr std::size_t info_side = 0;
        static constexpr std::size_t info_type = 4;
        // queue properties
        static constexpr std::size_t info_queue = 8;
        static constexpr std::size_t info_match = 10;
        static constexpr std::size_t info_fill = 12;
        static constexpr std::size_t info_cancel = 14;
        // time-in-force properties
        static constexpr std::size_t info_gtc = 16;
        static constexpr std::size_t info_ioc = 20;
        // execute properties
        static constexpr std::size_t info_aon = 24;

        bool is_buy() const { return test_bit(info, info_side); }
        void set_buy() { set_bit(info, info_side);}
        bool is_sell() const { return !test_bit(info, info_side); }
        void set_sell() { reset_bit(info, info_side); }
        bool is_limit() const { return test_bit(info, info_type); }
        void set_limit() { set_bit(info, info_type); }
        bool is_market() const { return !test_bit(info, info_type);; }
        void set_market() { reset_bit(info, info_type); }

        bool is_queue() const { return test_bit(info, info_queue); }
        void set_queue() { set_bit(info, info_queue); }
        bool is_match() const { return test_bit(info, info_match); }
        void set_match() { set_bit(info, info_match); }
        bool is_fill() const { return test_bit(info, info_fill); }
        void set_fill() { set_bit(info, info_fill); }
        bool is_cancel() const { return test_bit(info, info_cancel); }
        void set_cancel() { set_bit(info, info_cancel); }

        bool is_gtc() const { return test_bit(info, info_gtc); }
        void set_gtc() { set_bit(info, info_gtc); }
        bool is_ioc() const { return test_bit(info, info_ioc); }
        void set_ioc() { set_bit(info, info_ioc); }

        bool is_aon() const { return test_bit(info, info_aon); }
        void set_aon() { set_bit(info, info_aon); }

        template <typename... Args>
        BaseOrder(quant_type price, quant_type quant, Args... args)
            : base{price, quant, args...},
              info{}
        {
        }

        template <typename... Args>
        static BaseOrder create_buy(quant_type price, quant_type quant, Args... args)
        {
            BaseOrder order{price, quant, args...};
            order.set_buy();
            order.set_limit();
            return order;
        }

        template <typename... Args>
        static BaseOrder create_buy(quant_type quant, Args... args)
        {
            BaseOrder order{quant_type{}, quant, args...};
            order.set_buy();
            order.set_market();
            return order;
        }

        template <typename... Args>
        static BaseOrder create_sell(quant_type price, quant_type quant, Args... args)
        {
            BaseOrder order{price, quant, args...};
            order.set_sell();
            order.set_limit();
            return order;
        }

        template <typename... Args>
        static BaseOrder create_sell(quant_type quant, Args... args)
        {
            BaseOrder order{quant_type{}, quant, args...};
            order.set_sell();
            order.set_market();
            return order;
        }
    };

    /**
     * @brief Singleton tick counter
     *
     * @tparam T tick type
     */
    template <class T>
    class TickCounter
    {
    private:
        T counter;
        inline static TickCounter *instance = nullptr;

        TickCounter()
            : counter()
        {
            std::atexit(&clean);
        }

        static void clean()
        {
            delete instance;
            instance = nullptr;
        }

    public:
        static TickCounter &get()
        {
            if (!instance)
            {
                instance = new TickCounter();
            }
            return *instance;
        }

        T count()
        {
            return counter++;
        }

        TickCounter(const TickCounter &) = delete;
        TickCounter(TickCounter &&) = delete;
        TickCounter &operator=(const TickCounter &) = delete;
        TickCounter &operator=(TickCounter &&) = delete;
    };
}
